package com.example.codediff.analyzer;

import com.example.codediff.model.CallNode;
import com.example.codediff.model.CallPath;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.ClassReader;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.stereotype.Controller;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 调用链分析器
 * 负责构建调用图并查找调用链
 */
@Slf4j
@Component
public class CallChainAnalyzer {

    /**
     * 构建调用图
     */
    public Map<String, CallNode> buildCallGraph(File jarFile) throws IOException {
        Map<String, CallNode> callGraph = new HashMap<>();
        
        try (ZipFile zip = new ZipFile(jarFile)) {
            // 处理所有的class文件
            Enumeration<? extends ZipEntry> entries = zip.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if (!entry.getName().endsWith(".class")) {
                    continue;
                }

                try (InputStream in = zip.getInputStream(entry)) {
                    ClassReader reader = new ClassReader(in);
                    CallGraphVisitor visitor = new CallGraphVisitor(callGraph);
                    reader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
                }
            }
        }

        log.info("构建调用图完成，共有 {} 个方法节点", callGraph.size());
        return callGraph;
    }

    /**
     * 查找入口点（Controller方法）
     */
    public Set<CallNode> findEntryPoints(Map<String, CallNode> callGraph) {
        Set<CallNode> entryPoints = new HashSet<>();
        
        for (CallNode node : callGraph.values()) {
            String className = node.getClassName();
            try {
                Class<?> clazz = Class.forName(className);
                
                // 检查是否是Controller类
                if (clazz.isAnnotationPresent(Controller.class) ||
                    clazz.isAnnotationPresent(RestController.class)) {
                    entryPoints.add(node);
                }
            } catch (ClassNotFoundException e) {
                log.warn("无法加载类: {}", className);
            }
        }

        log.info("找到 {} 个入口点", entryPoints.size());
        return entryPoints;
    }

    /**
     * 查找从入口点到变更点的所有调用链
     */
    public List<CallPath> findCallChains(CallNode modifiedNode, 
                                       Map<String, CallNode> callGraph) {
        List<CallPath> result = new ArrayList<>();
        Set<String> visited = new HashSet<>();
        CallPath currentPath = new CallPath();
        
        // 使用DFS查找所有可能的调用链
        findCallChainsRecursive(modifiedNode, visited, currentPath, result);
        
        log.info("找到 {} 条到达变更点 {} 的调用链", 
                result.size(), modifiedNode.getReadableSignature());
        return result;
    }

    private void findCallChainsRecursive(CallNode currentNode, 
                                       Set<String> visited,
                                       CallPath currentPath,
                                       List<CallPath> result) {
        // 防止循环调用
        if (visited.contains(currentNode.getFullSignature())) {
            return;
        }
        
        // 添加当前节点到路径
        visited.add(currentNode.getFullSignature());
        currentPath.addNode(currentNode);
        
        // 如果没有调用者，说明可能是入口点
        if (currentNode.getCallers().isEmpty()) {
            result.add(currentPath.clone());
        } else {
            // 继续搜索调用者
            for (CallNode caller : currentNode.getCallers()) {
                CallPath newPath = currentPath.clone();
                findCallChainsRecursive(caller, new HashSet<>(visited), newPath, result);
            }
        }
        
        // 回溯
        visited.remove(currentNode.getFullSignature());
        currentPath.getPath().remove(currentPath.getPath().size() - 1);
    }
}
